package org.xian.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;

/**
 * @author : xian
 */
public class WakeUpSelectorTest implements Runnable {
    /**
     * Java NIO 编程的核心：多路复用器
     */
    private Selector selector;

    /**
     * 服务器的 channel
     */
    private ServerSocketChannel serverSocketChannel;

    private final int port = 8080;

    private boolean isStart = false;

    public WakeUpSelectorTest() {
        try {
            // 1、创建多路复用器
            this.selector = Selector.open();
            // 2、开启服务端 channel ，设置为非阻塞的，监听端口 8080
            this.serverSocketChannel = ServerSocketChannel.open();
            this.serverSocketChannel.configureBlocking(false);
            this.serverSocketChannel.bind(new InetSocketAddress(port));
            // 3、将服务端的 channel 注册到 selector ，监听 OP_ACCEPT 事件
            this.serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            this.isStart = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        WakeUpSelectorTest wakeUpSelectorTest = new WakeUpSelectorTest();

        Thread serverThread = new Thread(wakeUpSelectorTest);

        Thread wakeupThread = new Thread(() -> {
            try {
                // 这个线程十秒后唤醒 selector
                Thread.sleep(10000);
                wakeUpSelectorTest.getSelector().wakeup();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        });

        serverThread.start();
        wakeupThread.start();

        try {
            serverThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void run() {
        // 这里这里处理多路复用器 selector 的事件
        while (this.isStart) {
            try {
                // Selector 会在这里阻塞，直到有事件将其唤醒
                // 其他线程调用了 wakeup 也会被唤醒
                this.selector.select();
                System.out.println("Selector Wakeup");
                // 如果有其它线程调用了 #wakeup() 方法，但当前没有线程阻塞在 #select() 方法上，下个调用 #select() 方法的线程会立即被唤醒
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public Selector getSelector() {
        return selector;
    }
}
